package com.essence.grabiec104.grab.executor;

import com.essence.grabiec104.grab.executor.handler.AnalysisHandler;
import com.essence.grabiec104.grab.executor.handler.YKFrameComparisonHandler;
import com.essence.grabiec104.grab.service.AlarmService;
import org.pcap4j.core.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.EOFException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;


/**
 * @brief Pcap监听器
 * @details 监听网卡，获取数据包，组装成BytesFrame，并传递给下一级处理
 */
@Component
@DependsOn({"RouterService", "Publisher"})
public class PcapListenerRunner {
    private static final Logger LOG = LoggerFactory.getLogger(PcapListenerRunner.class);
    @Value("${grabber.pcap.ethNames}")
    private List<String> ethNames;
    @Value("${grabber.pcap.snaplen}")
    private int snaplen;
    @Value("${grabber.pcap.readTimeout}")
    private int readTimeout;
    @Value("${grabber.pcap.filter}")
    private String filter;
    @Value("${grabber.pcap.pcapPath}")
    private String pcapPath;
    // 网络设备对象集合
    private List<PcapNetworkInterface> nicGroup = new ArrayList<>();
    @Autowired
    private AsyncHandlerDispatcher asyncHandlerDispatcher;

    @Autowired
    private AlarmService alarmService;

    @PostConstruct
    public void init() throws PcapNativeException {
        for (String ethName : ethNames) {
            if (!checkDevs(ethName)) {
                // 找不到网络设备，退出程序
                throw new IllegalStateException("找不到网络设备: " + ethName);
            }
            PcapNetworkInterface nic = Pcaps.getDevByName(ethName);
            nicGroup.add(nic);
        }
        // 创建线程池，监听多个网卡，如果配置了pcapng文件，则分配一个独立线程，读取pcapng文件
        ExecutorService executor = SaveExecutor.addPool("PcapListener", nicGroup.size() + 1);

        if (pcapPath != null && !pcapPath.isEmpty()) {
            // 文件解析线程
            executor.execute(() -> {
                PacketListenerCodec listener = new PacketListenerCodec(snaplen, asyncHandlerDispatcher, alarmService, "pcapng");
                if (pcapPath != null && !pcapPath.isEmpty()) {
                    try {
                        PcapHandle fileHandle = Pcaps.openOffline(pcapPath, PcapHandle.TimestampPrecision.NANO);
                        while (true) {
                            try {
                                PcapPacket packet = fileHandle.getNextPacketEx();
                                listener.gotPacket(packet);
                            } catch (EOFException ex) {
                                LOG.info("文件读取结束", ex);
                                break;
                            }
                        }
                    } catch (TimeoutException | NotOpenException | PcapNativeException ex) {
                        LOG.error("读取数据包失败", ex);
                    }
                }
            });
        }
        // 网卡监听线程
        for (PcapNetworkInterface nic : nicGroup) {
            executor.execute(() -> {
                PacketListenerCodec listener = new PacketListenerCodec(snaplen, asyncHandlerDispatcher, alarmService, nic.getName());
                try (PcapHandle handle = nic.openLive(snaplen,
                        PcapNetworkInterface.PromiscuousMode.PROMISCUOUS,
                        readTimeout)) {
                    if (filter != null && !filter.isEmpty()) {
                        if (StringUtils.hasLength(filter)) {
                            handle.setFilter(filter, BpfProgram.BpfCompileMode.OPTIMIZE);
                        }
                    }
                    handle.loop(-1, listener);
                } catch (Exception ex) {
                    LOG.error("监听网卡失败", ex);
                }
            });
        }
    }

    private boolean checkDevs(String ethName) {
        try {
            List<PcapNetworkInterface> allDevs = Pcaps.findAllDevs();
            if (allDevs == null || allDevs.isEmpty()) {
                LOG.error("找不到网络设备");
                return false;
            }
            for (PcapNetworkInterface pcapNetworkInterface : allDevs) {
                LOG.info("设备名称: {}, 设备描述: {}", pcapNetworkInterface.getName(), pcapNetworkInterface.getDescription());
                if (ethName.equals(pcapNetworkInterface.getName())) {
                    return true;
                }
            }
            LOG.error("设备不存在: {}", ethName);
            return false;
        } catch (PcapNativeException e) {
            LOG.error("设备不存在: ", e);
            return false;
        }
    }

    @PreDestroy
    public void release() {
    }

}
